<template>
  <div class="graph-wrap" @click.stop="hideFn">
    <Toobar :graph="graph"></Toobar>

    <!-- 小地图 -->
    <div id="minimap" class="mini-map-container"></div>
    <!-- 画布 -->
    <div id="container" class="x6-workflow-node" />
    <!-- 右侧节点配置 -->
    <ConfigPanel
      class="right-config"
      :nodeData="nodeData"
      :saveType="saveType"
      :getGraphData="getGraphData"
    ></ConfigPanel>
    <!-- 右键 -->
    <Contextmenu
      v-if="showContextMenu"
      ref="menuBar"
      @callBack="contextMenuFn"
    ></Contextmenu>
  </div>
</template>
<script lang="ts" setup>
import {
  onUnmounted,
  defineProps,
  defineEmits,
  computed,
  ref,
  watch,
  onMounted,
  nextTick,
  defineExpose
} from 'vue';
import { Graph, Node, Path, Cell, Shape } from '@antv/x6';
import { useRoute } from 'vue-router';
import { register } from '@antv/x6-vue-shape';
import { Dnd } from '@antv/x6-plugin-dnd';
import { MiniMap } from '@antv/x6-plugin-minimap';
import { Scroller } from '@antv/x6-plugin-scroller';
import { Selection } from '@antv/x6-plugin-selection';

import ConfigPanel from './components/configPanel.vue';
import Contextmenu from './components/contextmenu.vue';
import DataBase from './components/nodeTheme/dataBase.vue';
import Toobar from './components/toobar.vue';
const emit = defineEmits(['saveGraph']);
const route = useRoute();
const props = defineProps({
  // 左侧引擎模版数据
  stencilData: {
    type: Object,
    default: () => {
      return {};
    },
  },
  graphData: {
    type: Array as any,
    default: () => {
      return [];
    },
  },
  // 保存类型
  saveType: {
    type: String,
    default: () => {
      return 'strategy';
    },
  },
}); 
const menuBar = ref<any>(null);
const graph = ref<any>('');
const timer = ref<any>('');
const showContextMenu = ref(<Boolean>false);
const dnd = ref<any>(null);
const nodeData = ref<any>({});
const nodeStatusList = ref<any>([]);
watch(
  () => props.graphData,
  (newVal) => {
    nodeStatusList.value = [];
    for (let i = 0; i < newVal.length; i++) {
      if (newVal[i].shape === 'dag-node') {
        if (newVal[i].data.status != null) {
          nodeStatusList.value.push({
            id: newVal[i].id,
            status: newVal[i].data.status,
          });
        }
      }
    }
    startFn(newVal);
  },
);

const isDetail = computed(() => {
  if (route.path === '/taskCenter/taskPlan/planDetails') {
    return true;
  } else {
    return false;
  }
});
onUnmounted(() => {
  clearTimeout(timer.value);
  timer.value = null;
  graph.value.dispose(); // 销毁画布
});
onMounted(() => {
  // 初始化 graph
  initGraph();
});
// 隐藏右键
const hideFn = () => {
  showContextMenu.value = false;
};
// 右键事件
const contextMenuFn = (type, itemData) => {
  switch (type) {
    case 'remove':
      if (itemData.type === 'edge') {
        graph.value.removeEdge(itemData.item.id);
      } else if (itemData.type === 'node') {
        graph.value.removeNode(itemData.item.id);
      }
      break;
  }
  showContextMenu.value = false;
};
// 注册vue组件节点   2.x 的写法
const registerCustomVueNode = () => {
  register({
    shape: 'dag-node',
    width: 185,
    height: 40,
    component: DataBase,
    ports: {
      groups: {
        top: {
          position: 'top',
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: '#C2C8D5',
              strokeWidth: 1,
              fill: '#fff',
            },
          },
        },
        bottom: {
          position: 'bottom',
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: '#C2C8D5',
              strokeWidth: 1,
              fill: '#fff',
            },
          },
        },
      },
    },
  });
};
// 注册HTML节点
const registerCustomHtmlNode = () => {
  const detection = require('@/static/images/detection.png');
  const imgCot = {
    0: require('@/static/images/wait-status.png'),
    1: require('@/static/images/progress-status.png'),
    2: require('@/static/images/success-status.png'),
    3: require('@/static/images/fail-status.png'),
    4: require('@/static/images/stop-status.png'),
  };

  Shape.HTML.register({
    shape: 'dag-node',
    width: 185,
    height: 40,
    effect: ['data'],
    html(cell) {
      const { label = '', status = 0 } = cell.getData();
      const statusImg = imgCot[status];

      const div = `
            <div class="node">
              <span class="left">
                <img src="${detection}" alt="" />
              </span>
              <span class="right">
                <span class="label" title="${label}">${label}</span>
                <span class="status">
                  <img src="${statusImg}" alt="" />
                </span>
              </span>
            </div>
          `;
      return div;
    },
    ports: {
      groups: {
        top: {
          position: 'top',
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: '#C2C8D5',
              strokeWidth: 1,
              fill: '#fff',
            },
          },
        },
        bottom: {
          position: 'bottom',
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: '#C2C8D5',
              strokeWidth: 1,
              fill: '#fff',
            },
          },
        },
      },
    },
  });
};
// 注册边
const registerCustomEdge = () => {
  Graph.registerEdge(
    'dag-edge',
    {
      inherit: 'edge',
      attrs: {
        line: {
          stroke: 'rgba(0, 0, 0, 0.3)',
          strokeWidth: 1,
          targetMarker: {
            name: 'block',
            width: 12,
            height: 8,
          },
        },
      },
    },
    true,
  );
};
// 注册连接器
const registerConnector = () => {
  Graph.registerConnector(
    'algo-connector',
    (s, e) => {
      const offset = 4;
      const deltaY = Math.abs(e.y - s.y);
      const control = Math.floor((deltaY / 3) * 2);

      const v1 = { x: s.x, y: s.y + offset + control };
      const v2 = { x: e.x, y: e.y - offset - control };

      return Path.normalize(
        `M ${s.x} ${s.y}
            L ${s.x} ${s.y + offset}
            C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
            L ${e.x} ${e.y}
            `,
      );
    },
    true,
  );
};
const initGraph = () => {
  registerCustomVueNode();
  registerCustomEdge();
  registerConnector();
  let element = document.getElementById('container');
  if (element) {
    const newgraph = new Graph({
      container: element,
      autoResize: true,
      // width: 800,
      // height: 600,
      background: {
        color: 'rgba(37, 50, 82, 0.1)', // 设置画布背景颜色
      },
      grid: {
        size: 10, // 网格大小 10px
        visible: false, // 渲染网格背景
      },
      // 画布平移， 不要同时使用 scroller 和 panning，因为两种形式在交互上有冲突。
      // panning: {
      //   enabled: true,
      //   eventTypes: ["leftMouseDown", "mouseWheel"],
      // },
      // 画布缩放
      mousewheel: {
        enabled: true,
        modifiers: 'ctrl',
        factor: 1.1,
        maxScale: 1.5,
        minScale: 0.5,
      },
      highlighting: {
        magnetAdsorbed: {
          name: 'stroke',
          args: {
            attrs: {
              fill: '#fff',
              stroke: '#31d0c6',
              strokeWidth: 4,
            },
          },
        },
      },
      connecting: {
        snap: true,
        allowBlank: false,
        allowLoop: false,
        highlight: true,
        connector: 'algo-connector',
        connectionPoint: 'anchor',
        anchor: 'center',
        validateMagnet({ magnet }) {
          return magnet.getAttribute('port-group') !== 'top';
        },
        createEdge() {
          return newgraph.createEdge({
            shape: 'dag-edge',
            attrs: {
              line: {
                strokeDasharray: '5 5',
              },
            },
            zIndex: -1,
          });
        },
      },
      // 点击选中 1.x 版本
      // selecting: {
      //   enabled: true,
      //   multiple: true,
      //   rubberEdge: true,
      //   rubberNode: true,
      //   modifiers: "shift",
      //   rubberband: true,
      // },
    });
    // 点击选中 2.x 版本
    newgraph.use(
      new Selection({
        multiple: true,
        rubberEdge: true,
        rubberNode: true,
        modifiers: 'shift',
        rubberband: true,
      }),
    );

    graph.value = newgraph;

    initAddon(); // 初始化 拖拽
    graphEvent();
    initScroller();
    initMiniMap();
  }
};
// 画布事件
const graphEvent = () => {
  const self = this;
  // 边连接/取消连接
  graph.value.on('edge:connected', ({ edge }) => {
    edge.attr({
      line: {
        strokeDasharray: '',
      },
    });
  });
  // 改变节点/边的数据时
  graph.value.on('node:change:data', ({ node }) => {
    const edges = graph.value.getIncomingEdges(node);
    const { status } = node.getData();
    edges?.forEach((edge) => {
      if (status === 'running') {
        edge.attr('line/strokeDasharray', 5);
        edge.attr('line/style/animation', 'running-line 30s infinite linear');
      } else {
        edge.attr('line/strokeDasharray', '');
        edge.attr('line/style/animation', '');
      }
    });
  });
  // 节点右键事件
  graph.value.on('node:contextmenu', ({ e, x, y, node, view }) => {
    showContextMenu.value = true;
    nextTick(() => {
      menuBar.value.initFn(e.pageX, e.pageY, {
        type: 'node',
        item: node,
      });
    });
  });
  // 边右键事件
  graph.value.on('edge:contextmenu', ({ e, x, y, edge, view }) => {
    showContextMenu.value = true;
    nextTick(() => {
      menuBar.value.initFn(e.pageX, e.pageY, {
        type: 'edge',
        item: edge,
      });
    });
  });
  // 节点单击事件
  graph.value.on('node:click', ({ e, x, y, node, view }) => {

    nextTick(() => {
      nodeData.value = {
        id: node.id,
        store: node.store,
      };
    });
  });
  // 鼠标抬起
  graph.value.on('node:mouseup', ({ e, x, y, node, view }) => {
    // self.$emit("saveGraph");
  });
  //平移画布时触发，tx 和 ty 分别是 X 和 Y 轴的偏移量。
  graph.value.on('translate', ({ tx, ty }) => {
    emit('saveGraph');
  });
  // 移动节点后触发
  graph.value.on('node:moved', ({ e, x, y, node, view }) => {
    emit('saveGraph');
  });
  // 移动边后触发
  graph.value.on('edge:moved', ({ e, x, y, node, view }) => {
    emit('saveGraph');
  });
};
// 初始化拖拽
const initAddon = () => {
  dnd.value = new Dnd({
    target: graph.value,
  });
};
// 开始拖拽
const startDragToGraph = () => {
  const node = graph.value.createNode(nodeConfig());
  dnd.value.start(node, props.stencilData.e);
};
// 节点配置
const nodeConfig = () => {
  const engineItem = props.stencilData.engineItem;
  const time = new Date().getTime();
  const attrs = {
    circle: {
      r: 4,
      magnet: true,
      stroke: '#C2C8D5',
      strokeWidth: 1,
      fill: '#fff',
    },
  };
  const top = {
    position: 'top',
    attrs,
  };
  const bottom = {
    pposition: 'bottom',
    attrs,
  };
  const itemsObj = [
    {
      id: `in-${time}`,
      group: 'top', // 指定分组名称
    },
    {
      id: `out-${time}`,
      group: 'bottom', // 指定分组名称
    },
  ];

  // 链接桩3种状态 1、in | 只允许被连  2、out | 只允许输出  3、any | 不限制
  let groups: any = {};
  let items: any = [];

  if (engineItem.top) {
    groups = {
      top,
    };
    items = [itemsObj[0]];
  }
  if (engineItem.bottom) {
    groups = {
      bottom,
    };
    items = [itemsObj[1]];
  }
  if (engineItem.top && engineItem.bottom) {
    groups = {
      top,
      bottom,
    };
    items = itemsObj;
  }

  let config = {
    shape: 'dag-node',
    width: 185,
    height: 40,
    attrs: {
      body: {
        fill: '#1D2035',
        stroke: 'rgba(255, 255, 255, 0.3)',
      },
      label: {
        text: engineItem.name,
        fill: 'rgba(255, 255, 255, 0.9)',
      },
    },
    ports: {
      groups,
      items,
    },
    data: {
      label: engineItem.name,
      engine: engineItem,
    },
  };
  // console.log(config, 33333);
  return config;
};
// 初始化节点/边
const init = (data = []) => {
  const cells: any = [];
  data.forEach((item: any) => {
    if (item.shape === 'dag-node') {
      cells.push(graph.value.createNode(item));
    } else {
      cells.push(graph.value.createEdge(item));
    }
  });
  graph.value.resetCells(cells);
};
// 显示节点状态
const showNodeStatus = async (statusList) => {
  // const status = statusList.shift();
  statusList?.forEach((item) => {
    const { id, status } = item;
    const node = graph.value.getCellById(id);
    const data = node.getData();
    node.setData({
      ...data,
      status: status,
    });
  });
  timer.value = setTimeout(() => {
    showNodeStatus(statusList);
  }, 3000);
};
const startFn = (item) => {
  timer.value && clearTimeout(timer.value);
  init(item);
  // this.showNodeStatus(Object.assign([], this.nodeStatusList));
  graph.value.centerContent();
};
// 获取画布数据
const getGraphData = () => {
  const { cells = [] } = graph.value.toJSON();
  let data: any = [];
  console.log(cells, 333);
  for (let i = 0; i < cells.length; i++) {
    let item = {};
    let cellsItem = cells[i];
    if (cellsItem.shape === 'dag-node') {
      let nodeType = 0; // 节点类型 0-下连接柱， 1-上下连接柱 ，2-上连接柱

      if (
        cellsItem.ports.items.length === 1 &&
        cellsItem.ports.items[0].group === 'bottom'
      ) {
        nodeType = 0;
      }
      if (cellsItem.ports.items.length === 2) {
        nodeType = 1;
      }
      if (
        cellsItem.ports.items.length === 1 &&
        cellsItem.ports.items[0].group === 'top'
      ) {
        nodeType = 2;
      }

      item = {
        id: cellsItem.id,
        shape: cellsItem.shape,
        x: cellsItem.position.x,
        y: cellsItem.position.y,
        ports: cellsItem.ports.items,
        data: {
          ...cellsItem.data,
          type: 'node',
          nodeType: nodeType,
        },
      };
    } else {
      item = {
        id: cellsItem.id,
        shape: cellsItem.shape,
        source: cellsItem.source,
        target: cellsItem.target,
        data: {
          type: 'edge',
        },
        zIndex: 0,
      };
    }
    data.push(item);
  }
  return data;
};
defineExpose({
  getGraphData
})
// 提供方法给子组件
const initScroller = () => {
  graph.value.use(
    new Scroller({
      enabled: true,
      pageVisible: true,
      pageBreak: false,
      pannable: true,
    }),
  );
};
// 初始化小地图
const initMiniMap = () => {
  let element = document.getElementById('minimap');
  if (element) {
    graph.value.use(
      new MiniMap({
        container: element,
        width: 220,
        height: 140,
        padding: 10,
      }),
    );
  }
  // graph.value.use(
  //   new MiniMap({
  //     container:  document.getElementById('minimap'),
  //     width: 220,
  //     height: 140,
  //     padding: 10,
  //   }),
  // );
};
</script>
<style lang="scss" scoped>
.graph-wrap {
  width: 100%;
  height: 100%;
  min-height: 600px;
  position: relative;
  background-color: $-white;

  #container {
    width: 100%;
    height: 100%;
  }

  .right-config {
    position: absolute;
    top: 0px;
    right: 0px;
  }
}
</style>
<style lang="scss">
.x6-workflow-node {
  .node {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    // background-color: #1d2035;
    border-radius: 8px;
    box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06);

    .left {
      flex-shrink: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 40px;
      height: 40px;
      border-radius: 8px 0px 0px 8px;
      box-sizing: border-box;
      border: 1px solid rgba(220, 223, 230);
      background: rgba(42, 230, 255, 0.15);

      img {
        width: 22px;
        height: 22px;
      }
    }

    .right {
      height: 100%;
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border: 1px solid rgba(220, 223, 230);
      border-radius: 0px 8px 8px 0px;
      border-left: 0;
      padding: 0px 5px;

      .label {
        flex: 1;
        display: inline-block;
        flex-shrink: 0;
        color: #666;
        font-size: 12px;
        overflow: hidden; //超出文本隐藏
        text-overflow: ellipsis; ///超出部分省略号显示
        display: -webkit-box; //弹性盒模型
        -webkit-box-orient: vertical; //上下垂直
        -webkit-line-clamp: 2; //自定义行数
      }

      .status {
        width: 18px;
        height: 18px;
        flex-shrink: 0;
        margin-left: 5px;

        img {
          width: 18px;
          height: 18px;
        }
      }
    }
  }
  .node.progress .status img {
    animation: spin 1s linear infinite;
  }

  .x6-node-selected .node {
    border-color: #2ae6ff;
    border-radius: 8px;
    box-shadow: 0 0 0 3px #d4e8fe;
  }

  .x6-node-selected .node.running {
    border-color: #2ae6ff;
    border-radius: 8px;
    // box-shadow: 0 0 0 4px #ccecc0;
  }

  .x6-node-selected .node.success {
    border-color: #52c41a;
    border-radius: 8px;
    // box-shadow: 0 0 0 4px #ccecc0;
  }

  .x6-node-selected .node.failed {
    border-color: #ff4d4f;
    border-radius: 8px;
    // box-shadow: 0 0 0 4px #fedcdc;
  }

  .x6-edge:hover path:nth-child(2) {
    stroke: #1890ff;
    stroke-width: 1px;
  }

  .x6-edge-selected path:nth-child(2) {
    stroke: #1890ff;
    stroke-width: 1.5px !important;
  }

  @keyframes running-line {
    to {
      stroke-dashoffset: -1000;
    }
  }

  @keyframes spin {
    from {
      transform: rotate(0deg);
    }

    to {
      transform: rotate(360deg);
    }
  }
}

// 小地图
.mini-map-container {
  position: absolute;
  bottom: 12px;
  right: 10px;
  width: 220px;
  height: 140px;
  opacity: 1;
  // background: #fff;
  border: 1px solid rgba(255, 255, 255, 0.3);

  .status {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    margin-left: 5px;

    img {
      width: 18px;
      height: 18px;
    }
  }
}

.x6-widget-minimap {
  background: rgba(37, 50, 82, 0.1) !important;
}

.x6-widget-minimap-viewport {
  border: 1px solid #0289f7 !important;
}

.x6-widget-minimap-viewport-zoom {
  border: 1px solid #0289f7 !important;
}

.x6-widget-minimap .x6-graph {
  box-shadow: none !important;
}

.x6-graph-scroller.x6-graph-scroller-paged .x6-graph {
  box-shadow: none !important;
}

// .x6-graph-scroller::-webkit-scrollbar {
//   width: 8px;
//   height: 8px;
//   /**/
// }
// .x6-graph-scroller::-webkit-scrollbar-track {
//   background: rgb(239, 239, 239);
//   border-radius: 2px;
// }
// .x6-graph-scroller::-webkit-scrollbar-thumb {
//   background: #bfbfbf;
//   border-radius: 10px;
// }
// .x6-graph-scroller::-webkit-scrollbar-thumb:hover {
//   background: #999;
// }
// .x6-graph-scroller::-webkit-scrollbar-corner {
//   background: rgb(239, 239, 239);
// }
</style>
